//
// Created by wyh on 2025-06-14 22:36.
//
#include <fstream>
#include <sstream>
#include <iostream>
#include "utility/ini_file.h"

using namespace std;
using namespace wyh::utility;

IniFile::IniFile(const std::string &filename)
{
    load(filename);
}

bool IniFile::load(const string &filename)
{
    m_sections.clear();
    m_filename = filename;

    ifstream ifs(filename);
    if (ifs.fail())//当第一次运行时，自动创建ini文件
    {
//        throw logic_error("loading ini file failed:"+filename);
        ofstream ofs(filename);
        if(ofs.fail())
        {
            throw logic_error("loading ini file failed:"+filename);
        }
        else
        {
            ofs.close();
            ifs.open(filename);
            if (ifs.fail())
            {
                throw logic_error("loading ini file failed:"+filename);
            }
        }
    }

    string name;
    string line;
    while (getline(ifs, line))
    {
        line = trim(line);
        if (line.empty())
        {
            continue;
        }

        if (line[0] == '#' || line[0] == ';') {//这里是注释
            continue;
        }

        if(line[0]=='[')//这里就是一个section
        {
            auto pos = line.find_first_of(']');
            if (pos == string::npos)
            {
                throw logic_error("invalid section:" + line);
            }
            name = trim(line.substr(1, pos - 1));
            m_sections[name]=Section();
        }
        else//这里就是 key=value
        {
            auto pos = line.find_first_of('=');
            if (pos == string::npos)
            {
                throw logic_error("inivalid option:" + line);
            }
            string key = line.substr(0, pos);
            key = trim(key);

            string val = line.substr(pos + 1);
            val = trim(val);
            m_sections[name][key] = val;
        }
    }

    return true;
}

std::string IniFile::trim(std::string s)
{
    if (s.empty())
    {
        return s;
    }

    s.erase(0, s.find_first_not_of(" \r\n"));
    s.erase(s.find_last_not_of(" \r\n")+1);
    return s;
}

std::string IniFile::str() const
{
    stringstream ss;
    for (const auto &section: m_sections)
    {
        ss<<"["<<section.first<<"]"<<endl;
        for (const auto &option: section.second)
        {
            ss<<option.first<<" = "<<(string)(option.second)<<endl;
        }
        ss<<endl;
    }
    return ss.str();
}

void IniFile::show() const
{
    cout<<str();
}

Value &IniFile::get(const string &section, const string &key)
{
    return m_sections[section][key];
}

void IniFile::set(const string &section, const string &key, const Value &value)
{
    m_sections[section][key] = value;
}

bool IniFile::has(const string &section)
{
    return m_sections.find(section) != m_sections.end();
}

bool IniFile::has(const string &section, const string &key)
{
    auto it = m_sections.find(section);
    if (it == m_sections.end())
    {
        return false;
    }

    return it->second.find(key)!=it->second.end();
}

void IniFile::remove(const string &section)
{
    m_sections.erase(section);
}

void IniFile::remove(const string &section, const string &key)
{
    auto it = m_sections.find(section);
    if (it == m_sections.end())
    {
        return;
    }
    it->second.erase(key);
}

void IniFile::clear()
{
    m_sections.clear();
}

void IniFile::save(const string &filename)
{
    ofstream ofs(filename);
    if (ofs.fail())
    {
        throw logic_error("saving ini file failed:" + filename);
    }

    ofs << str();
    ofs.close();
}


